﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Reflection;
using System.Web.Script.Serialization;
using System.IO;
using System.Xml.Serialization;
using System.Net;

namespace NETXPF.Web
{
    /// <summary>
    /// Provides utilities for implementing AJAX-compatible web services using XML or JSON over HTTP POST
    /// </summary>
    public static class WebServices
    {
        /// <summary>
        /// Processes a request using REST-like semantics, with a response that is AJAX-friendly.
        /// Forwards this call to a class that implements a WCF-like service contract.
        /// Note: Endpoint name is case insensitive but operation name is case sensitive.
        /// Special query-string parameters: op (the name of the method), format (one of: xml [default], json), callback (optional name of JavaScript callback, for JSON only)
        /// Service methods must respect the following format:
        ///     public [serializabletype] MethodName(ParamFormat format, string parameter);
        /// </summary>
        /// <param name="context">Request</param>
        /// <param name="endpointName">Name of the endpoint that corresponds to this service type</param>
        /// <param name="service">Type of the service class to call</param>
        /// <exception cref="ArgumentException">Thrown when an invalid argument is detected</exception>
        public static void ProcessServiceRequest(HttpContext context, string endpointName, Type service)
        {
            if (context.Request.Url.AbsolutePath.ToLower().EndsWith(endpointName.ToLower()))
            {
                string op = context.Request.QueryString["op"];
                if (string.IsNullOrEmpty(op) || service.GetMethod(op) == null)
                {
                    throw new ArgumentException("Invalid operation specified");
                }
                object serviceObj = Activator.CreateInstance(service);

                //read the input data as string and let the service handle serialization/deserialization
                string param;
                using (StreamReader sr = new StreamReader(context.Request.InputStream))
                {
                    param = sr.ReadToEnd();
                }
                ParamFormat format = ParamFormat.XML;
                if (string.IsNullOrEmpty(context.Request.QueryString["format"]) ||
                    context.Request.QueryString["format"].ToLower().Equals("xml"))
                {
                    format = ParamFormat.XML;
                }
                else if (context.Request.QueryString["format"].ToLower().StartsWith("json"))
                {
                    format = ParamFormat.JSON;
                }

                //call the web service
                object result = service.GetMethod(op).Invoke(serviceObj, new object[] { format, param });

                if(format == ParamFormat.XML)
                {
                    context.Response.ContentType = "application/xml";
                    NETXPF.Library.Persist.ToXML(context.Response.OutputStream, result);
                }
                else if(format == ParamFormat.JSON)
                {
                    context.Response.ContentType = "application/json";
                    using (System.IO.StreamWriter sw = new StreamWriter(context.Response.OutputStream))
                    {
                        JavaScriptSerializer js = new JavaScriptSerializer();
                        if (!string.IsNullOrEmpty(context.Request.QueryString["callback"]))
                        {
                            sw.Write("{0}(", context.Request.QueryString["callback"]);
                        }
                        sw.Write(js.Serialize(result));
                        if (!string.IsNullOrEmpty(context.Request.QueryString["callback"]))
                        {
                            sw.Write(");");
                        }
                    }
                }
                context.Response.End();
            }
        }
    }
    /// <summary>
    /// The format of the parameter string
    /// </summary>
    public enum ParamFormat
    {
        /// <summary>
        /// XML data format (use XmlSerializer)
        /// </summary>
        XML,
        /// <summary>
        /// JSON data format (use JavaScriptSerializer)
        /// </summary>
        JSON
    }
}
